#include"Log.hpp"
#include"Sock.hpp"
#include<unistd.h>
#include<cerrno>
#include<ctime>
#include<poll.h>
#include<vector>
#include<iostream>
#include<cstring>
using namespace bobo;

#define FD_NONE -1

class PollServer
{
    void Routine()
    {
        for(int i = 0; i < _nfds; ++i)
        {
            if(_fds[i].fd == FD_NONE) continue; // 1. 去掉不合法的fd
            if(_fds[i].revents  & POLLIN) // 2. 合法的就一定就绪了?不一定
            {
                //指定的fd，读事件就绪
                // 读事件就绪：连接事件到来，accept
                if(_fds[i].fd  == _listenSock) Accepter();
                else Recver(i);
            }
        }
    }
    void Accepter()
    {
        uint16_t clientPort = 0;
        std::string clientIp;
        // listensock上面的读事件就绪了，表示可以读取了
        // 获取新连接了
        int sock = Sock::Accept(_listenSock, &clientPort, &clientIp); // 这里在进行accept会不会阻塞？不会！
        if(sock < 0)
        {
            logMessage(ERROR, "accept fail...[%d]%s\n", errno, strerror(errno));
            return;
        }
        logMessage(DEBUG, "get a new line success : [%s:%d] : %d\n", clientIp.c_str(), clientPort, sock);
        int pos = 1;
        for(; pos < _nfds; ++pos)
        {
            if(_fds[pos].fd == FD_NONE) break;
        }
        if(pos == _nfds)
        {
            logMessage(WARNING, "%s:%d", "select server already full，close: %d\n", sock);
            close(sock);            
        }
        else
        {
            _fds[pos].fd = sock;
            _fds[pos].events = POLLIN; //******************
        }
    }
    void Recver(int pos)
    {
        // 读事件就绪：INPUT事件到来、recv，read
        logMessage(DEBUG, "message in, get IO event: %d\n", _fds[pos].fd);
        // 暂时先不做封装, 此时select已经帮我们进行了事件检测，fd上的数据一定是就绪的，即 本次 不会被阻塞
        // 这样读取有bug吗？有的，你怎么保证以读到了一个完整包文呢？
        char buffer[1024];
        ssize_t n = recv(_fds[pos].fd, buffer, sizeof buffer - 1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            logMessage(DEBUG, "client[%d]# %s", _fds[pos].fd, buffer);
        }
        else if(n == 0)
        {
            logMessage(DEBUG, "client[%d] quit, me too...\n", _fds[pos].fd);
            // 1. 我们也要关闭不需要的fd
            close(_fds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
           _fds[pos].fd = FD_NONE;
           _fds[pos].events = 0;
        }
        else
        {
            logMessage(WARNING, "%d sock recv error, %d : %s\n", _fds[pos].fd, errno, strerror(errno));
            // 1. 我们也要关闭不需要的fd
            close(_fds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
            _fds[pos].fd = FD_NONE;
            _fds[pos].events = 0;
        }
    }
    void DebugPrint()
    {
        std::cout << "_fd_array[]: ";
        for(int i = 0; i < _nfds; i++)
        {
            if(_fds[i].fd == FD_NONE) continue;
            std::cout << _fds[i].fd << " ";
        }
        std::cout << std::endl;
    }
public:
    PollServer(const uint16_t& port = 8080, int nfds = 100, int timeout = 1000)
        :_port(port)
        ,_nfds(nfds)
        ,_timeout(timeout)
    {
        _listenSock = Sock::Socket();
        // 取消TIME_WAIT
        int opt = 1;
        setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);

        Sock::Bind(_listenSock, _port);
        Sock::Listen(_listenSock);
        logMessage(DEBUG,"%s","create base socket success\n");

        _fds = new pollfd[_nfds];
        for(int i = 0; i < _nfds; ++i) _fds[i] = {-1, 0, 0};
        _fds[0] = {_listenSock, POLLIN};
    }
    void start()
    {
        DebugPrint();
        
        while(true)
        {
            int n = poll(_fds, _nfds, _timeout);
            switch(n)
            {
                case 0:
                    logMessage(NORMAL, "time out...\n");
                    break;
                case -1:
                    logMessage(WARNING, "select fail[%d]: %s\n", errno, strerror(errno));
                    break;
                default:
                    // 成功的
                    logMessage(DEBUG, "get a new link event...\n"); // 为什么会一直打印连接到来呢？连接已经建立完成，就绪了，但是你没有取走，select要一直通知你！
                    Routine();
                    break;
            }
        }
    }
    ~PollServer()
    {
        if(_listenSock >= 0) close(_listenSock);
        if(_fds) delete[] _fds;
    }
private:
    int _listenSock;
    uint16_t _port;
    struct pollfd* _fds;
    int _nfds;
    int _timeout;
};